home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d11 / frasrc14.arc / ENCODER.C < prev    next >
Text File  |  1990-08-02  |  16KB  |  528 lines

  1. /*
  2.     encoder.c - GIF Encoder and associated routines
  3.  
  4. */
  5.  
  6. #include <stdlib.h>
  7. #include <stdio.h>
  8. #include <string.h>
  9. #include <ctype.h>
  10.  
  11. #include "fractint.h"
  12. #include "fractype.h"
  13.  
  14. extern int initbatch;
  15. extern struct fractal_info save_info;    /*  for saving data in GIF file */
  16. extern char far *resume_info;        /* pointer to resume info if allocated */
  17. extern int  resume_len;         /* length of resume info */
  18. extern char FormName[40];        /* formula name */
  19. #define EXTOVHEAD 16
  20.  
  21. extern    int    xdots, ydots;        /* # of dots on the screen  */
  22. extern    int    colors;         /* maximum colors available */
  23. extern    int    dotmode;        /* so we can detect disk-video */
  24. extern    int    warn;            /* warnings on/off */
  25. extern    int    resave_flag;        /* resaving after a timed save */
  26. extern    int    timedsave;        /* if doing an auto save */
  27.  
  28. extern unsigned char dacbox[256][3];    /* Video-DAC (filled in by SETVIDEO) */
  29. extern int    daclearn, daccount;    /* used by the color-cyclers */
  30. extern int    reallyega;        /* "reall-an-EGA" flag */
  31. extern int    extraseg;        /* used by Save-to-GIF routines */
  32. extern char potfile[];        /* potential file name TW 7/19/89 */
  33.  
  34. extern int gif87a_flag;     /* if 1, supress all GIF extension blocks */
  35.  
  36. /*
  37.             Save-To-Disk Routines (GIF)
  38.  
  39. GIF and 'Graphics Interchange Format' are trademarks (tm) of Compuserve
  40. Incorporated, an H&R Block Company.
  41.  
  42.  
  43. The following routines perform the GIF encoding when the 's' key is pressed.
  44. The routines refer to several variables that are declared elsewhere
  45. [colors, xdots, ydots, and 'dacbox'], and rely on external routines to
  46. actually read and write screen pixels [getcolor(x,y) and putcolor(x,y,color)].
  47. (Writing pixels is just stuffed in here as a sort of visual status report,
  48. and has nothing to do with any GIF function.)    They also rely on the
  49. existence of an externally-defined 64K dataspace and they use the routines
  50. 'toextra()' and 'cmpextra()' to deal with that dataspace (in the same manner
  51. as 'memcpy()' and 'memcmp()' would).   Otherwise, they perform a generic
  52. GIF-encoder function.
  53.  
  54. Note that these routines use small string- and hash-tables, and "flush"
  55. the GIF entries whenever the hash-table gets two-thirds full or the string
  56. table gets full.   They also use the GIF encoding technique of limiting the
  57. encoded string length to a specific size, "adding" a string to the hash table
  58. at that point even if a matching string exists ("adding" is in quotes, because
  59. if a matching string exists we can increment the code counter but safely throw
  60. the duplicate string away, saving both string space and a hash table entry).
  61.  
  62.    This results in relatively good speed and small data space, but at the
  63. expense of compression efficiency (filesize).    These trade-offs could be
  64. adjusted by modifying the #DEFINEd variables below.
  65.  
  66. Note that the 'strlocn' and 'teststring' routines are declared
  67. to be external just so that they can be defined (and the space re-used)
  68. elsewhere.  The actual declarations are in the assembler code.
  69.  
  70. */
  71.  
  72. #define MAXTEST   100        /* maximum single string length */
  73. #define MAXSTRING 64000     /* total space reserved for strings */
  74.                 /* maximum number of strings available */
  75. #define MAXENTRY  5003        /* (a prime number is best for hashing) */
  76.  
  77. extern unsigned int strlocn[MAXENTRY];
  78. extern unsigned char teststring[MAXTEST];
  79. extern unsigned char block[266];   /* GIF-encoded blocks go here */
  80.  
  81. static int numsaves = 0;    /* For adjusting 'save-to-disk' filenames */
  82.  
  83. static FILE *out;
  84.  
  85. static int lentest, lastentry, numentries, numrealentries;
  86. static unsigned int nextentry;
  87. static int clearcode, endcode;
  88. static unsigned int hashcode;
  89.  
  90. static unsigned char blockcount;
  91. static int startbits, codebits, bytecount, bitcount;
  92.  
  93. static char paletteBW[] = {            /* B&W palette */
  94.       0,  0,  0, 63, 63, 63,
  95.     };
  96. static char paletteCGA[] = {            /* 4-color (CGA) palette  */
  97.       0,  0,  0, 21, 63, 63, 63, 21, 63, 63, 63, 63,
  98.     };
  99. static char paletteEGA[] = {            /* 16-color (EGA/CGA) pal */
  100.       0,  0,  0,  0,  0, 42,  0, 42,  0,  0, 42, 42,
  101.      42,  0,  0, 42,  0, 42, 42, 21,  0, 42, 42, 42,
  102.      21, 21, 21, 21, 21, 63, 21, 63, 21, 21, 63, 63,
  103.      63, 21, 21, 63, 21, 63, 63, 63, 21, 63, 63, 63,
  104.     };
  105. int paletteVGA[] = {            /* VGA palette - to DAC registers */
  106.     0, 1, 2, 3, 4, 5,20, 7,56,57,58,59,60,61,62,63,
  107.     };
  108.  
  109. savetodisk(filename)            /* save-to-disk routine */
  110. char *filename;
  111. {
  112. char tmpmsg[41]; /* before openfile in case of overrun */
  113. char openfile[80], openfiletype[10];
  114. char tmpfile[80];
  115. int newfile;
  116. int i, j, ydot, xdot, color, outcolor1, outcolor2, outcolor1s, outcolor2s;
  117. unsigned int hashentry;
  118. unsigned char bitsperpixel, x;
  119. int entrynum;
  120. int last_colorbar;
  121. char far *resume_ptr;
  122.  
  123. if (extraseg == 0) {            /* not enough memory for this */
  124.     buzzer(2);
  125.     return(0);
  126.     }
  127.  
  128. restart:
  129.  
  130. strcpy(openfile,filename);        /* decode and open the filename */
  131. strcpy(openfiletype,DEFAULTFRACTALTYPE);/* determine the file extension */
  132. for (i = 0; i < strlen(openfile); i++)
  133.     if (openfile[i] == '.') {
  134.         strcpy(openfiletype,&openfile[i]);
  135.         openfile[i] = 0;
  136.         }
  137. if (resave_flag == 0 && ++numsaves > 1) {
  138.     updatesavename(openfile);       /* secondary saves? new filename */
  139.     strncpy(filename, openfile, strlen(openfile));
  140.     }
  141.  
  142. strcat(openfile,openfiletype);
  143.  
  144. strcpy(tmpfile,openfile);
  145. if (access(openfile,0) != 0) /* file doesn't exist */
  146.     newfile = 1;
  147. else { /* file already exists */
  148.     if (warn && resave_flag == 0)
  149.         goto restart;
  150.     if (access(openfile,2) != 0) {
  151.         buzzer(2);
  152.         sprintf(tmpmsg," ?? Can't write %s ",openfile);
  153.         texttempmsg(tmpmsg);
  154.         return(0);
  155.         }
  156.     newfile = 0;
  157.     i = strlen(tmpfile);
  158.     while (--i >= 0 && tmpfile[i] != '\\')
  159.         tmpfile[i] = 0;
  160.     strcat(tmpfile,"fractint.tmp");
  161.     }
  162. if ((out=fopen(tmpfile,"wb")) == NULL) {
  163.     buzzer(2);
  164.     sprintf(tmpmsg," ?? Couldn't create %s ",tmpfile);
  165.     texttempmsg(tmpmsg);
  166.     return(0);
  167.     }
  168.  
  169. if (dotmode == 11) {            /* disk-video */
  170.     movecursor(2,0);
  171.     printf("...saving...");
  172.     }
  173.  
  174. bitsperpixel = 0;            /* calculate bits / pixel */
  175. for (i = colors; i >= 2; i /= 2 )
  176.     bitsperpixel++;
  177.  
  178. startbits = bitsperpixel+1;        /* start coding with this many bits */
  179. if (colors == 2)
  180.     startbits++;            /* B&W Klooge */
  181.  
  182. clearcode = 1 << (startbits - 1);    /* set clear and end codes */
  183. endcode = clearcode+1;
  184.  
  185. outcolor1 = 0;                /* use these colors to show progress */
  186. outcolor2 = 1;                /* (this has nothing to do with GIF) */
  187. if (colors > 2) {
  188.     outcolor1 = 2;
  189.     outcolor2 = 3;
  190.     }
  191. if ((numsaves & 1) == 0) {        /* reverse the colors on alt saves */
  192.     i = outcolor1;
  193.     outcolor1 = outcolor2;
  194.     outcolor2 = i;
  195.     }
  196. outcolor1s = outcolor1;
  197. outcolor2s = outcolor2;
  198.  
  199. if (gif87a_flag == 1)
  200.     fwrite("GIF87a",1,6,out);              /* old GIF Signature */
  201. else
  202.     fwrite("GIF89a",1,6,out);              /* new GIF Signature */
  203.  
  204. fwrite(&xdots,2,1,out);         /* screen descriptor */
  205. fwrite(&ydots,2,1,out);
  206. x = 128 + ((6-1)<<4) + (bitsperpixel-1); /* color resolution == 6 bits worth */
  207. fwrite(&x,1,1,out);
  208. i = 0;
  209. fwrite(&i,1,1,out);
  210. fwrite(&i,1,1,out);
  211.  
  212. if (colors == 256) {            /* write out the 256-color palette */
  213.     if (dacbox[0][0] != 255)    /* got a DAC - must be a VGA */
  214.         shftwrite(dacbox,colors);
  215.     else                /* uh oh - better fake it */
  216.         for (i = 0; i < 256; i += 16)
  217.             shftwrite(paletteEGA,16);
  218.     }
  219. if (colors == 2)            /* write out the B&W palette */
  220.     shftwrite(paletteBW,colors);
  221. if (colors == 4)            /* write out the CGA palette */
  222.     shftwrite(paletteCGA,colors);
  223. if (colors == 16)            /* Either EGA or VGA */
  224.     if (dacbox[0][0] != 255) {    /* got a  DAC - must be a VGA */
  225.         if (reallyega)        /* well, maybe really an EGA */
  226.             shftwrite(dacbox,colors);
  227.         else
  228.             for (i = 0; i < colors; i++)
  229.                 shftwrite(dacbox[paletteVGA[i]],1);
  230.         }
  231.         else            /* no DAC - must be an EGA */
  232.             shftwrite(paletteEGA,colors);
  233.  
  234. fwrite(",",1,1,out);                    /* Image Descriptor */
  235. i = 0;
  236. fwrite(&i,2,1,out);
  237. fwrite(&i,2,1,out);
  238. fwrite(&xdots,2,1,out);
  239. fwrite(&ydots,2,1,out);
  240. i = 0;
  241. fwrite(&i,1,1,out);
  242.  
  243. bitsperpixel = startbits - 1;        /* raster data starts here */
  244. fwrite(&bitsperpixel,1,1,out);
  245.  
  246. codebits = startbits;            /* start encoding */
  247.  
  248. raster(9999);                /* initialize the raster routine */
  249.  
  250. inittable();                /* initialize the LZW tables */
  251.  
  252. for (ydot = 0; ydot < ydots; ydot++) {    /* scan through the dots */
  253.     for (xdot = 0; xdot < xdots; xdot++) {
  254.         color = getcolor(xdot,ydot);    /* get the next dot */
  255.         teststring[0] = ++lentest;
  256.         teststring[lentest] = color;
  257.         if (lentest == 1) {        /* root entry? */
  258.             lastentry = color;
  259.             continue;
  260.             }
  261.         if (lentest == 2)        /* init   the hash code */
  262.             hashcode = 301 * (teststring[1]+1);
  263.         hashcode *= (color + lentest);    /* update the hash code */
  264.         hashentry = ++hashcode % MAXENTRY;
  265.         for( i = 0; i < MAXENTRY; i++) {
  266.             if (++hashentry >= MAXENTRY) hashentry = 0;
  267.             if (cmpextra(strlocn[hashentry]+2,
  268.                 teststring,lentest+1) == 0)
  269.                     break;
  270.             if (strlocn[hashentry] == 0) i = MAXENTRY;
  271.             }
  272.         /* found an entry and string length isn't too bad */
  273.         if (strlocn[hashentry] != 0 && lentest < MAXTEST-3) {
  274.             fromextra(strlocn[hashentry],&entrynum,2);
  275.             lastentry = entrynum;
  276.             continue;
  277.             }
  278.         raster(lastentry);            /* write entry */
  279.         numentries++;        /* act like you added one, anyway */
  280.         if (strlocn[hashentry] == 0) {    /* add new string, if any */
  281.             entrynum = numentries+endcode;
  282.             strlocn[hashentry] = nextentry;
  283.             toextra(nextentry, &entrynum,2);
  284.             toextra(nextentry+2,
  285.                 teststring,lentest+1);
  286.             nextentry += lentest+3;
  287.             numrealentries++;
  288.             }
  289.         teststring[0] = 1;        /* reset current entry */
  290.         teststring[1] = color;
  291.         lentest = 1;
  292.         lastentry = color;
  293.  
  294.         if ((numentries+endcode) == (1<<codebits))
  295.             codebits++;         /* use longer encoding */
  296.  
  297.         if ( numentries + endcode > 4093 ||    /* out of room? */
  298.             numrealentries > (MAXENTRY*2)/3 ||
  299.             nextentry > MAXSTRING-MAXTEST-5) {
  300.             raster(lastentry);        /* flush & restart */
  301.             inittable();
  302.             }
  303.         }
  304.     if (dotmode != 11) {            /* supress this on disk-video */
  305.         if ((ydot & 4) == 0) {
  306.             if (++outcolor1 >= colors) outcolor1 = 0;
  307.             if (++outcolor2 >= colors) outcolor2 = 0;
  308.             }
  309.         for (i = 0; 250*i < xdots; i++) {    /* display vert status bars */
  310.                             /*   (this is NOT GIF-related)    */
  311.             /* PB Changed following code to xor color, so that
  312.                image can be restored at end and resumed
  313.                putcolor(      i,ydot,outcolor1);
  314.                putcolor(xdots-1-i,ydot,outcolor2);
  315.             */
  316.             putcolor(i,ydot,getcolor(i,ydot)^outcolor1);
  317.             putcolor(xdots-1-i,ydot,getcolor(xdots-1-i,ydot)^outcolor2);
  318.             }
  319.         last_colorbar = ydot;
  320.         }
  321.     if (kbhit())                /* keyboard hit - bail out */
  322.         ydot = 9999;
  323.     }
  324.  
  325. raster(lastentry);            /* tidy up - dump the last code */
  326.  
  327. raster(endcode);            /* finish the map */
  328.  
  329. i = 0;                    /* raster data ends here */
  330. fwrite(&i,1,1,out);
  331.  
  332. if (gif87a_flag == 0) { /* store non-standard fractal info */
  333.     if (ydot >= 9999)
  334.         save_info.calc_status = 0; /* partial save is not resumable */
  335.     save_info.tot_extend_len = EXTOVHEAD + sizeof(save_info);
  336.     if (resume_info != NULL && save_info.calc_status == 2) {
  337.         put_extend_hdr(2,resume_len); /* resume info block, 002 */
  338.         resume_ptr = resume_info;
  339.         i = resume_len;
  340.         while (--i >= 0)
  341.             fputc(*(resume_ptr++),out);
  342.         fwrite("\0",1,1,out);
  343.         save_info.tot_extend_len += EXTOVHEAD + resume_len;
  344.         }
  345.     if (save_info.fractal_type == FORMULA || save_info.fractal_type == FFORMULA) {
  346.         put_extend_hdr(3,40); /* additional formula info block, 003 */
  347.         fwrite(FormName,40,1,out);
  348.         fwrite("\0",1,1,out);
  349.         save_info.tot_extend_len += EXTOVHEAD + 40;
  350.         }
  351.     put_extend_hdr(1,sizeof(save_info)); /* main and last block, 001 */
  352.     fwrite(&save_info,sizeof(save_info),1,out);
  353.     fwrite("\0",1,1,out);
  354.     }
  355.  
  356. fwrite(";",1,1,out);                    /* GIF Terminator */
  357.  
  358. fclose(out);
  359.  
  360. if (newfile == 0) { /* now we're safely done, replace the real file */
  361.     if (unlink(openfile) != 0) {
  362.         buzzer(2);
  363.         sprintf(tmpmsg," ?? Can't replace %s ",openfile);
  364.         texttempmsg(tmpmsg);
  365.         return(0);
  366.         }
  367.     if (rename(tmpfile,openfile) != 0) {
  368.         buzzer(2);
  369.         sprintf(tmpmsg," ?? Can't rename %s ",tmpfile);
  370.         texttempmsg(tmpmsg);
  371.         return(0);
  372.         }
  373.     }
  374.  
  375. if (dotmode != 11) {            /* supress this on disk-video */
  376.     outcolor1 = outcolor1s;
  377.     outcolor2 = outcolor2s;
  378.     for (j = 0; j <= last_colorbar; j++) {
  379.         if ((j & 4) == 0) {
  380.             if (++outcolor1 >= colors) outcolor1 = 0;
  381.             if (++outcolor2 >= colors) outcolor2 = 0;
  382.             }
  383.         for (i = 0; 250*i < xdots; i++) { /* clear vert status bars */
  384.             putcolor(i,j,getcolor(i,j)^outcolor1);
  385.             putcolor(xdots-1-i,j,getcolor(xdots-1-i,j)^outcolor2);
  386.             }
  387.         }
  388.     }
  389. else {                    /* disk-video */
  390.     movecursor(2,0);
  391.     printf("            ");
  392.     }
  393. if (ydot < 9999) {            /* signal normal end */
  394.     if (timedsave == 0) {
  395.         buzzer(0);
  396.         if (initbatch == 0) {
  397.             sprintf(tmpmsg," File saved as %s ",openfile);
  398.             texttempmsg(tmpmsg);
  399.             }
  400.         }
  401.     }
  402. else {
  403.     buzzer(1);
  404.     getakey();            /* read the (interrupt) key-press */
  405.     sprintf(tmpmsg," *INTERRUPTED* File %s ", openfile);
  406.     texttempmsg(tmpmsg);
  407.     }
  408. }
  409.  
  410. shftwrite(color,numcolors)        /* shift IBM colors to GIF format */
  411. unsigned char color[];
  412. int numcolors;
  413. {
  414. unsigned char thiscolor;
  415. int i,j;
  416. for (i = 0; i < numcolors; i++)
  417.     for (j = 0; j < 3; j++) {
  418.         thiscolor = color[3*i+j];
  419.         thiscolor = thiscolor << 2;
  420.         thiscolor += (thiscolor >> 6);
  421.         /*    fwrite(&thiscolor,1,1,out); */
  422.         fputc(thiscolor,out);
  423.         }
  424. }
  425.  
  426. inittable()                /* routine to init tables */
  427. {
  428. int i;
  429.  
  430. raster(clearcode);            /* signal that table is initialized */
  431.  
  432. numentries = 0;             /* initialize the table */
  433. numrealentries = 0;
  434. nextentry = 1;
  435. lentest = 0;
  436. codebits = startbits;
  437.  
  438. toextra(0,"\0",1);                      /* clear the hash entries */
  439. for (i = 0; i < MAXENTRY; i++)
  440.     strlocn[i] = 0;
  441.  
  442. }
  443.  
  444. raster(code)                /* routine to block and output codes */
  445. unsigned int code;
  446. {
  447. unsigned int icode, i, j;
  448.  
  449. if (code == 9999) {            /* special start-up signal */
  450.     bytecount = 0;
  451.     bitcount = 0;
  452.     for (i = 0; i < 266; i++)
  453.         block[i] = 0;
  454.     return(0);
  455.     }
  456.  
  457. icode = code << bitcount;        /* update the bit string */
  458. block[bytecount  ] |= (icode & 255);
  459. block[bytecount+1] |= ((icode>>8) & 255);
  460. icode = (code>>8) << bitcount;
  461. block[bytecount+2] |= ((icode>>8) & 255);
  462. bitcount += codebits;
  463. while (bitcount >= 8) {         /* locate next starting point */
  464.     bitcount -= 8;
  465.     bytecount++;
  466.     }
  467.  
  468. if (bytecount > 250 || code == endcode) {    /* time to write a block */
  469.     if (code == endcode)
  470.         while (bitcount > 0) {        /* if EOF, find the real end */
  471.             bitcount -= 8;
  472.             bytecount++;
  473.             }
  474.     i = bytecount;
  475.     blockcount = i;
  476.     fwrite(&blockcount,1,1,out);        /* write the block */
  477.     fwrite(block,i,1,out);
  478.     bytecount = 0;                /* now re-start the block */
  479.     for (j = 0; j < 5; j++)         /* (may have leftover bits) */
  480.         block[j] = block[j+i];
  481.     for (j = 5; j < 266; j++)
  482.         block[j] = 0;
  483.     }
  484. }
  485.  
  486.  
  487. updatesavename(name)                     /* go to the next name */
  488. char *name;
  489. {
  490.   char *save, *hold = name + strlen(name) - 1;        /* start at the end */
  491.   int i =  0;
  492.  
  493.   while(hold >= name && (*hold == ' ' || isdigit(*hold))) /* skip backwards */
  494.     hold--;
  495.  
  496.   hold++;                         /* recover first digit */
  497.  
  498.   while (*hold == '0')                                /* skip leading zeros */
  499.     hold++;
  500.  
  501.   save = hold;
  502.  
  503.   while (*save)                      /* check for all nines */
  504.   {
  505.     if (*save != '9')
  506.       break;
  507.     save++;
  508.   }
  509.  
  510.   if (!*save)               /* if the whole thing is nines then back */
  511.     save = hold - 1;           /* up one place. Note that this will eat */
  512.                    /* your last letter if you go to far.    */
  513.   else
  514.     save = hold;
  515.  
  516.   itoa(atoi(hold) + 1, save, 10);            /* increment the number */
  517. }
  518.  
  519. put_extend_hdr(int block_id,int block_len) /* for .fra extension blocks */
  520. {
  521.   char header[15];
  522.   strcpy(header,"!\377\013fractint");
  523.   sprintf(&header[11],"%03u",block_id);
  524.   fwrite(header,14,1,out);
  525.   fwrite(&block_len,1,1,out);
  526. }
  527.  
  528.